package com.huawei.esdk.tp.authorize;

import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import com.huawei.esdk.platform.abnormalevent.itf.IAbnormalevent;
import com.huawei.esdk.platform.authorize.itf.IAuthorize;
import com.huawei.esdk.platform.common.MessageContext;
import com.huawei.esdk.platform.common.SDKErrorCode;
import com.huawei.esdk.platform.common.SDKResult;
import com.huawei.esdk.platform.common.ThreadLocalHolder;
import com.huawei.esdk.platform.common.bean.aa.AccountInfo;
import com.huawei.esdk.platform.common.bean.abnormalevent.AbnormaleventBean;
import com.huawei.esdk.platform.common.bean.callback.CallbackItfInfo;
import com.huawei.esdk.platform.common.bean.callback.CallbackMessage;
import com.huawei.esdk.platform.common.bean.config.UserConfig;
import com.huawei.esdk.platform.common.constants.ESDKConstant;
import com.huawei.esdk.platform.common.constants.ESDKErrorCodeConstant;
import com.huawei.esdk.platform.common.exception.SDKException;
import com.huawei.esdk.platform.common.utils.Base64Utils;
import com.huawei.esdk.platform.common.utils.DateUtils;
import com.huawei.esdk.platform.nemgr.base.NotifyCollector;
import com.huawei.esdk.platform.usermgr.itf.IUserLockManager;
import com.huawei.esdk.platform.usermgr.itf.IUserManager;
import com.huawei.esdk.tp.base.BaseService;
import com.huawei.esdk.tp.common.ErrInfo;
import com.huawei.esdk.tp.common.TPAAUtils;
import com.huawei.esdk.tp.common.constants.InfoKeys;
import com.huawei.esdk.tp.common.constants.TPConstant;
import com.huawei.esdk.tp.domain.model.AuthorizeMode;
import com.huawei.esdk.tp.domain.model.bean.AuthorizeInfo;

public class AuthenticateService extends BaseService
{
    private static final Logger LOGGER = Logger.getLogger(AuthenticateService.class);
    
    private IUserManager userManager;
    
    private IUserLockManager userLockManager;
    
    private IAuthorize authorize;
    
    private IAbnormalevent abnormaleventManager;
    
    private NotifyCollector collector;
    
    public SDKResult<LoginRequestBean> appLoginRequest(String userName)
    {
        LOGGER.debug("appLoginRequest start");
        String sdkSessionId = (String)ThreadLocalHolder.get().getEntities().get(InfoKeys.SDK_SESSION_ID.toString());
        SDKResult<LoginRequestBean> result = new SDKResult<LoginRequestBean>();
        LoginRequestBean bean = new LoginRequestBean();
        result.setResult(bean);
        
        String hashType;
        byte[] randomKey;
        int resultCode;
        if (authorize.isLocalAuth("tp"))
        {
            //生成随机数
            randomKey = TPAAUtils.generateRandomKey();
            hashType = "SHA256";
            resultCode = 0;
        }
        else
        {
            //透传鉴权是通过设备生成随机数
            AuthorizeMode mode = new AuthorizeMode();
            SDKResult<AuthorizeInfo> devResult = mode.loginRequest(userName);
            
            hashType = devResult.getResult().getHashType();
            randomKey = devResult.getResult().getRandomSequence();
            resultCode = (int)devResult.getErrCode();
            
            SessionManager.getInstance()
                .getSDKSession(sdkSessionId)
                .setSMCSessionId(devResult.getResult().getSessionId());
        }
        // 存储随机数
        saveUserAndRandomKey(sdkSessionId, userName, randomKey);
        
        bean.setHashType(hashType);
        bean.setRandomkey(randomKey);
        result.setErrCode(resultCode);
        
        LOGGER.debug("appLoginRequest end");
        return result;
    }
    
    private void saveUserAndRandomKey(String sdkSessionId, String userName, byte[] random)
    {
        String randomSequence = Base64Utils.encode(random);
        LOGGER.debug("RandomKey=" + randomSequence);
        
        SessionManager sessionMgr = SessionManager.getInstance();
        SessionInfo sessionInfo = sessionMgr.getSDKSessionAvoidNull(sdkSessionId);
        sessionInfo.setRandomSequence(randomSequence);
        sessionInfo.setUserName(userName);
    }
    
    public SDKErrorCode appAuthenticate(byte[] encryptPassword)
        throws SDKException
    {
        LOGGER.debug("appAuthenticate start");
        SDKErrorCode sdkErrorCode = new SDKErrorCode();
        MessageContext mc = ThreadLocalHolder.get();
        SessionManager sessionMgr = SessionManager.getInstance();
        String sessionId = (String)mc.getEntities().get(InfoKeys.SDK_SESSION_ID.toString());
        String userName = SessionManager.getInstance().getSDKSessionAvoidNull(sessionId).getUserName();
        int successFlag = ErrInfo.APPPWD_ERRORCODE;
        
        //将用户信息存入线程变量
        AccountInfo acctInfo = new AccountInfo();
        mc.getEntities().put(ESDKConstant.ACCT_INFO_ESDK, acctInfo);
        acctInfo.setUserId(userName);
        
        if (authorize.isLocalAuth("tp"))
        {
            String pwd = null;
            UserConfig user = userManager.getUserById(userName);
            if (user != null)
            {
                pwd = user.getPassword();
                //Check whether the password is valid
                successFlag = checkPassword(userName, pwd, encryptPassword);
                if (0 == successFlag)
                {
                    //下发消息通知设备管理器连接SMC
                    publishMessage(TPConstant.DEV_ID_SMC, ESDKConstant.NOTIFY_ITFNAME_CONNECT, TPConstant.DEV_ID_SMC);
                    Long loginStatus = (Long)mc.getEntities().get(TPConstant.SMC_LOGIN_STATUS);
                    successFlag = null != loginStatus ? loginStatus.intValue() : successFlag;
                }
                
            }
        }
        else
        {
            acctInfo.setPassword(Base64Utils.encode(encryptPassword));
            mc.getEntities().put(InfoKeys.SMC_SESSION_ID.toString(),
                sessionMgr.getSDKSession(sessionId).getSMCSessionId());
            
            deviceManager.connect(TPConstant.DEV_ID_SMC);
            successFlag = ((Long)mc.getEntities().get(TPConstant.SMC_LOGIN_STATUS)).intValue();
        }
        
        
        if (0 == successFlag)
        {
            //保存登录状态
            sessionMgr.getSDKSessionAvoidNull(sessionId).setLogged(true);
        }
        
        processAbnormalEvent(successFlag, userName);
        sdkErrorCode.setErrCode(successFlag);
        
        LOGGER.debug("appAuthenticate end");
        return sdkErrorCode;
    }
    
    private void processAbnormalEvent(int successFlag, String userName)
    {
        AbnormaleventBean bean = new AbnormaleventBean();
        bean.setObjName(userName);
        bean.setOccurrence(IAbnormalevent.FAIL_TO_AUTHENTICATE);
        
        if (0 != successFlag)
        {
            // add by taochunlong 鉴权失败记入异常信息管理            
            bean.setOccurTime(new Date());
            bean.setExceptionMessage("");
            abnormaleventManager.occurException(bean.getObjName() + "_" + bean.getOccurrence(), bean);
        }
        else
        {
            // add by taochunlong 鉴权成功后若有异常信息则将信终止
            bean.setEndTime(new Date());
            abnormaleventManager.endException(bean.getObjName() + "_" + bean.getOccurrence(), bean);
        }
    }
    
    private int checkPassword(String userId, String usrPwd, byte[] srcBytesPwd)
        throws SDKException
    {
        LOGGER.debug("checkPassword start");
        if (userLockManager.isUserLocked(userId))
        {
            if (!userLockManager.tryUnlockUser(userId))
            {
                //Account is locked
                return ESDKErrorCodeConstant.ERROR_CODE_SDK_AUTHORIZE_ACCT_LOCK;
            }
        }
        
        // get SHA256 encrypted password
        MessageContext mc = ThreadLocalHolder.get();
        SessionManager sessionMgr = SessionManager.getInstance();
        String randomSequence =
            sessionMgr.getSDKSessionAvoidNull(((String)mc.getEntities().get(InfoKeys.SDK_SESSION_ID.toString())))
                .getRandomSequence();
        
        String finalPwd = TPAAUtils.getEncryptPassword(usrPwd, randomSequence);
        String srcStringPwd = Base64Utils.encode(srcBytesPwd);
        
        LOGGER.debug("checkPassword end");
        if (srcStringPwd.equals(finalPwd))
        {
            userLockManager.resetRetryTimes(userId);
            return 0;
        }
        else
        {
            userLockManager.setRetryTime(userId, DateUtils.getCurrentDate());
            userLockManager.increaseRetryTimes(userId);
            if (userLockManager.reachMaxRetryTimes(userId))
            {
                userLockManager.lockUser(userId);
            }
        }
        
        return ErrInfo.APPPWD_ERRORCODE;
    }
    
    public SDKErrorCode appLogoutRequest()
    {
        LOGGER.debug("appLogoutRequest start");
        SDKErrorCode sdkErrorCode = new SDKErrorCode();
        sdkErrorCode.setErrCode(0);
        
        try
        {
            MessageContext mc = ThreadLocalHolder.get();
            SessionManager sessionMgr = SessionManager.getInstance();
            
            // 删除SDKSessionID
            String sdkSession = (String)mc.getEntities().get(InfoKeys.SDK_SESSION_ID.toString());
            
            List<String> uris = sessionMgr.getSDKSessionAvoidNull(sdkSession).getTerminalUriList();
            for (String uri : uris)
            {
                publishMessage(uri, ESDKConstant.NOTIFY_ITFNAME_DISCONNECT, uri);
            }
            
            publishMessage(TPConstant.DEV_ID_SMC, ESDKConstant.NOTIFY_ITFNAME_DISCONNECT, TPConstant.DEV_ID_SMC);
            if (!authorize.isLocalAuth("tp"))
            {
                sdkErrorCode.setErrCode(((Long)mc.getEntities().get(TPConstant.SMC_LOGOUT_STATUS)).intValue());
            }
            sessionMgr.removeSDKSession(sdkSession);
        }
        catch (Exception e)
        {
            LOGGER.error("logout error", e);
            sdkErrorCode.setErrCode(ErrInfo.SDK_SYSTEM_ERRORCODE);
            sdkErrorCode.setDescription(ErrInfo.SDK_SYSTEM_ERRORDESC);
        }
        LOGGER.debug("appLogoutRequest end");
        return sdkErrorCode;
    }
    
    public IUserManager getUserManager()
    {
        return userManager;
    }
    
    public void setUserManager(IUserManager userManager)
    {
        this.userManager = userManager;
    }
    
    public IUserLockManager getUserLockManager()
    {
        return userLockManager;
    }
    
    public void setUserLockManager(IUserLockManager userLockManager)
    {
        this.userLockManager = userLockManager;
    }
    
    public IAbnormalevent getAbnormaleventManager()
    {
        return abnormaleventManager;
    }
    
    public void setAbnormaleventManager(IAbnormalevent abnormaleventManager)
    {
        this.abnormaleventManager = abnormaleventManager;
    }
    
    public NotifyCollector getCollector()
    {
        return collector;
    }
    
    public void setCollector(NotifyCollector collector)
    {
        this.collector = collector;
    }
    
    private void publishMessage(String devId, String itfName, String processorId)
    {
        CallbackMessage message = new CallbackMessage();
        CallbackItfInfo callbackItfInfo = new CallbackItfInfo();
        callbackItfInfo.setDevId(devId);
        callbackItfInfo.setItfName(itfName);
        callbackItfInfo.setNotifyMsgType(ESDKConstant.NOTIFY_MSG_TYPE_ESDK_EVENT);
        callbackItfInfo.setProcessorId(processorId);
        message.setCallbackItfInfo(callbackItfInfo);
        collector.publishNotify(message);
    }

    public IAuthorize getAuthorize()
    {
        return authorize;
    }
    
    public void setAuthorize(IAuthorize authorize)
    {
        this.authorize = authorize;
    }

    public SDKErrorCode keepAlive()
        throws SDKException
    {
        SDKErrorCode result = new SDKErrorCode();
        
        MessageContext mc = ThreadLocalHolder.get();
        if (null == mc)
        {
            result.setErrCode(ErrInfo.SESSIONID_ERRORCODE);
            
            // 修改findbugs问题，这边就需要返回result，否则下面的代码有可能会因为mc为空而抛出空指针异常
            return result;
        }
        String sessionId = (String)mc.getEntities().get(InfoKeys.SDK_SESSION_ID.toString());
        SessionManager sessionMgr = SessionManager.getInstance();
        mc.getEntities().put(InfoKeys.SMC_SESSION_ID.toString(), sessionMgr.getSDKSession(sessionId).getSMCSessionId());
        if (authorize.isLocalAuth("tp"))
        {
            result.setErrCode(0);
        }
        else
        {
            AuthorizeMode mode = new AuthorizeMode();
            result = mode.keepAlive();
        }
        return result;
    }	
}
